/******************************************************************************
 * This file is part of libemb.
 *
 * libemb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libemb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with libemb.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: Embedme
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://git.oschina.net/cblock/embedme
 * Copyright 2014~2017 @ ShenZhen ,China
*******************************************************************************/
#include "Tracer.h"
#include "BmpImage.h"
#include "File.h"
#include "Utils.h"

#include <stdlib.h>

#define RGB565(r,g,b)    ((((uint16)(r&0x001F))<<11)|(((uint16)(g&0x003F))<<5)|((uint16)(b&0x001F)))
#define RGB565_R(rgb)    (((rgb)&0xF800)>>8)   
#define RGB565_G(rgb)    (((rgb)&0x07E0)>>3)
#define RGB565_B(rgb)    (((rgb)&0x001F)<<3)

BmpImage::BmpImage():
m_dataLen(0),
m_data(NULL)
{
    memset(&m_header,0,sizeof(m_header));
}

BmpImage::~BmpImage()
{
    if (m_data!=NULL) 
    {
        free(m_data);
        m_data=NULL;
    }
}

/**
 *  \brief  加载BMP文件
 *  \param  fileName BMP文件名
 *  \return 成功返回true,失败返回false
 *  \note   目前只支持24位BMP
 */
bool BmpImage::loadFormFile(std::string fileName)
{
    if (fileName.empty()) 
    {
        TRACE_ERR_CLASS("BMP file name empty!\n");
        return false;
    }
    File bmpFile;
    if(!bmpFile.open(fileName.c_str(), IO_MODE_RD_ONLY))
    {
        TRACE_ERR_CLASS("Open BMP file failed:%s.\n",fileName.c_str());
        return false;
    }

    int ret=bmpFile.readData((char*)&m_header,sizeof(m_header));
    if (ret!=sizeof(m_header)) 
    {
        TRACE_ERR_CLASS("Read header error when loading BMP file failed:%s.\n",fileName.c_str());
        return false;
    }
    if (m_header.m_type[0]!='B' || m_header.m_type[1]!='M') 
    {
        TRACE_ERR_CLASS("BMP type error \"%c%c\", BMP file:%s.\n",m_header.m_type[0],m_header.m_type[1],fileName.c_str());
        return false;
    }
    if (Utils::isBigEndian()) 
    {
        /* BMP头格式为小端字节序,因此需要转换 */
        m_header.m_size = Utils::endianReverse(m_header.m_size);
        m_header.m_reserved1 = Utils::endianReverse(m_header.m_reserved1);
        m_header.m_reserved2 = Utils::endianReverse(m_header.m_reserved2);
        m_header.m_offBits = Utils::endianReverse(m_header.m_offBits);
        m_header.m_headerSize = Utils::endianReverse(m_header.m_headerSize);
        m_header.m_width = Utils::endianReverse(m_header.m_width); 
        m_header.m_height = Utils::endianReverse(m_header.m_height);
        m_header.m_flames = Utils::endianReverse(m_header.m_flames);
        m_header.m_bits = Utils::endianReverse(m_header.m_bits);
        m_header.m_compression = Utils::endianReverse(m_header.m_compression);
        m_header.m_sizeImage = Utils::endianReverse(m_header.m_sizeImage);
        m_header.m_xppm = Utils::endianReverse(m_header.m_xppm);
        m_header.m_yppm = Utils::endianReverse(m_header.m_yppm);
        m_header.m_colorIndex = Utils::endianReverse(m_header.m_colorIndex);
        m_header.m_colorImportant = Utils::endianReverse(m_header.m_colorImportant);
    }
    if (m_header.m_size != bmpFile.getSize())
    {
        TRACE_ERR_CLASS("Bmp size error(%d,%d), BMP file:%s.\n",fileName.c_str());
        return false;
    }
    return true;
}

/**
 *  \brief  加载RGB原始图像数据
 *  \param  width 原始图像宽度
 *  \param  height 原始图像高度
 *  \param  rawFormat 原始图像格式
 *  \param  rawData 原始图像数据地址
 *  \param  size 原始图像数据长度
 *  \return 成功返回true,失败返回false
 *  \note   目前只支持RGB565格式的原始数据
 */
bool BmpImage::loadFromRawData(int width,int height,int rawFormat,const char* rawData,int size)
{
    switch (rawFormat) {
    case IMAGE_RAW_FORMAT_RGB565:
        break;
    default:
        TRACE_ERR_CLASS("Unsupport raw format:%d.\n",rawFormat);
        return false; 
    }
    /* 默认生成非压缩的24位位图 */
    m_dataLen = width*height*3;
    m_header.m_type[0]='B';
    m_header.m_type[1]='M';
    m_header.m_size = m_dataLen+ 0x36; 
    m_header.m_reserved1 = 0;
    m_header.m_reserved2 = 0;
    m_header.m_offBits = 0x36;
    m_header.m_headerSize = 40;
    m_header.m_width = width;
    m_header.m_height = height;
    m_header.m_flames = 1;
    m_header.m_bits = 24;
    m_header.m_compression = 0;
    m_header.m_sizeImage = m_dataLen;
    m_header.m_xppm=0;
    m_header.m_yppm=0;
    m_header.m_colorIndex = 0;
    m_header.m_colorImportant = 0;

    if (m_data!=NULL) 
    {
        free(m_data);
        m_data = NULL;
    }
    m_data = (char*)malloc(m_dataLen);
    if (m_data==NULL) 
    {
        TRACE_ERR_CLASS("Can't alloc memory for BMP!\n");
        return false;
    }
    /*        RGB坐标系                  BMP坐标系
     *    ###################      ###################
     * 00 #00+01+02+03+04+05#   03 #18+19+20+21+22+23#
     * 01 #06+07+08+09+10+11#   02 #12+13+14+15+16+17#
     * 02 #12+13+14+15+16+17#   01 #06+07+08+09+10+11#
     * 03 #18+19+20+21+22+23#   00 #00+01+02+03+04+05#
     *    ###################      ###################
     */
    if (rawFormat == IMAGE_RAW_FORMAT_RGB565) 
    {
        /* bmp数据是从左下角的像素开始的,而RGB数据是从左上角开始的 */
        for (int y=0; y < height; y++) 
        {
            int rbgStart = y*width;            /* RGB行首坐标 */
            int bmpStart = (height-1-y)*width; /* BMP行首坐标 */
            for (int x = 0; x < width; x++)
            {
                /* BMP中RGB颜色顺序是:B-G-R */
                uint16 rgb=*(uint16*)(rawData+(rbgStart+x)*2);
                m_data[(bmpStart+x)*3] = RGB565_B(rgb);
                m_data[(bmpStart+x)*3+1] = RGB565_G(rgb);
                m_data[(bmpStart+x)*3+2] = RGB565_R(rgb);
            }
        }
    }
    else
    {
        TRACE_ERR_CLASS("Can't support raw format:%d\n",rawFormat);
        return false;
    }
    return true;
}

/**
 *  \brief  保存BMP文件
 *  \param  fileName BMP文件名
 *  \return 成功返回true,失败返回false
 *  \note   目前只支持24位BMP图像
 */
bool BmpImage::saveAsFile(std::string fileName)
{
    if (fileName.empty() || 
        m_data==NULL ||
        m_dataLen<=0) 
    {
        TRACE_ERR_CLASS("Parameter error,fileName(%s),m_dataLen(%d).\n",fileName.c_str(),m_dataLen);
        return false;
    }
    File bmpFile;
    if (!bmpFile.open(fileName.c_str(), IO_MODE_REWR_ORNEW)) 
    {
        TRACE_ERR_CLASS("Open file failed: %s.\n",fileName.c_str());
        return false;
    }
    BmpHeader_S header=m_header;
    if (Utils::isBigEndian()) 
    {
        /* BMP文件头是小端字节序的 */
        header.m_size = Utils::host2LitEndian(m_header.m_size);
        header.m_reserved1 = Utils::host2LitEndian(m_header.m_reserved1);
        header.m_reserved2 = Utils::host2LitEndian(m_header.m_reserved2);
        header.m_offBits = Utils::host2LitEndian(m_header.m_offBits);
        header.m_headerSize = Utils::host2LitEndian(m_header.m_headerSize);
        header.m_width = Utils::host2LitEndian(m_header.m_width);
        header.m_height = Utils::host2LitEndian(m_header.m_height);
        header.m_flames = Utils::host2LitEndian(m_header.m_flames);
        header.m_bits = Utils::host2LitEndian(m_header.m_bits);
        header.m_compression = Utils::host2LitEndian(m_header.m_compression);
        header.m_sizeImage = Utils::host2LitEndian(m_header.m_sizeImage);
        header.m_xppm = Utils::host2LitEndian(m_header.m_xppm);
        header.m_yppm = Utils::host2LitEndian(m_header.m_yppm);
        header.m_colorIndex = Utils::host2LitEndian(m_header.m_colorIndex);
        header.m_colorImportant = Utils::host2LitEndian(m_header.m_colorImportant);
    }
    int ret = bmpFile.writeData((const char*)&header,sizeof(header));
    if (ret!=sizeof(header)) 
    {
        TRACE_ERR_CLASS("Write BMP header failed:%s.\n",fileName.c_str());
        bmpFile.close();
        return false;
    }
    ret = bmpFile.writeData(m_data, m_dataLen);
    if (ret!=m_dataLen) 
    {
        TRACE_ERR_CLASS("Write BMP data failed:%s.\n",fileName.c_str());
        bmpFile.close();
        return false;
    }
    bmpFile.close();
    return true;
}

/**
 *  \brief  获取BMP图像宽度
 *  \param  none
 *  \return 图像宽度
 *  \note   none
 */
int BmpImage::getWidth()
{
    return m_header.m_width;
}
/**
 *  \brief  获取BMP图像高度
 *  \param  none
 *  \return 图像高度
 *  \note   none
 */
int BmpImage::getHeight()
{
    return m_header.m_height;
}
